#pragma once
#include <iostream>
#include <unordered_map>
#include <vector>
#include <cassert>
#include <sstream>

#include "../comm/Logger.hpp"
#include "../comm/Util.hpp"
// ducument version
namespace ns_model
{
    using namespace ns_log;
    using namespace ns_util;
    struct Question
    {
// #define debug
#ifdef debug
        void Show()
        {
            std::cout << "######################" << std::endl;
            std::cout << number << " " << title << " " << star << " " << cpu_limit << " " << mem_limit << std::endl;
            std::cout << "######################" << std::endl;
            std::cout << header << std::endl;
            std::cout << "######################" << std::endl;
            std::cout << tail << std::endl;
            std::cout << "######################" << std::endl;
            std::cout << dest << std::endl;
            std::cout << "######################" << std::endl;
        }
#endif
        std::string number; // 题目编号
        std::string title;  // 题目标题
        std::string star;   // 题目难度
        int cpu_limit;      // 时间复杂度
        int mem_limit;      // 空间复杂度
        std::string header; // 预先设置的代码
        std::string tail;   // 测试用例
        std::string dest;   // 题目描述
    };

    const static std::string defquestions_list_dir_path = "./questions/";
    const static std::string defquestions_list_name = "questions.list";
    const static std::string defdest_name = "dest";
    const static std::string defheader_name = "header";
    const static std::string deftail_name = "tail";

    // 用于和数据进行交互，管理核心的数据和状态
    class Model
    {
    private:
        bool LoadQuestionList(const std::string &questions_list)
        {
            // 1.读取所有题目列表
            std::ifstream in(questions_list);
            if (!in.is_open())
            {
                LOG(LogLevel::FATAL) << "加载题库失败";
                 return false;
            }

            //  getline默认不读取'\n' && geiline返回值支持重载
            std::string line;
            // 2. 读取一行题目列表
            while (getline(in, line))
            {
                std::stringstream ss(line);
                std::string number; // 题目编号
                std::string title;  // 题目标题
                std::string star;   // 题目难度
                int cpu_limit;      // 时间复杂度
                int mem_limit;      // 空间复杂度
                ss >> number >> title >> star >> cpu_limit >> mem_limit;
                // 3. 根据题目编号找到header和tail和dest
                std::string one_question_header_path = PathUtil::HeaderSRC(number, defheader_name);
                std::string header;
                if (!FileUtil::ReadFile(one_question_header_path, &header, true))
                    return false;

                std::string one_question_tail_path = PathUtil::TailSRC(number, deftail_name);
                std::string tail;
                if (!FileUtil::ReadFile(one_question_tail_path, &tail, true))
                    return false;

                std::string one_question_dest_path = PathUtil::DestTxt(number, defdest_name);
                std::string dest;
                if (!FileUtil::ReadFile(one_question_dest_path, &dest, true))
                    return false;

                // 4. 填入题目属性
                Question q = {number, title, star, cpu_limit, mem_limit, header, tail, dest};
                // 5. 插入题目集
                _questions.insert(std::make_pair(number, q));
            }
            in.close();
            LOG(LogLevel::INFO) << "加载题库成功";
            return true;
        }

    public:
        Model()
        {
            std::string questions_list = defquestions_list_dir_path + defquestions_list_name;
            assert(LoadQuestionList(questions_list));
        }

        // #define debug
        #ifdef debug
        void ShowQuestions()
        {
            for (auto &q : _questions)
            {
                std::cout << q.first << ":";
                q.second.Show();
            }
        }
        #endif

        bool GetAllQuestions(std::vector<Question> *out)
        {
            if (out == nullptr || _questions.empty())
                return false;
            for (auto &q : _questions)
                out->emplace_back(q.second);
            return true;
        }

        bool GetOneQuestion(const std::string &number, Question *out)
        {
            if (out == nullptr || _questions.empty())
                return false;
            const auto &it = _questions.find(number);
            if (it == _questions.end())
                return false;

            *out = it->second;
            return true;
        }

        ~Model() {}

    private:
        std::unordered_map<std::string, Question> _questions;
    };
}